#include "syntaxtree.h"
#include "basicparser.h"
#include "parser.h"
#include "tokenizer.h"
#include "utills.h"
#include "errors.h"

namespace CCompiler
{

    void displayStmt(SynStatement *stmt, std::ostream &stream, int ident)
    {
        if(stmt) stmt->print(stream, ident);
        else
        {
            makeIdent(stream, ident);
            stream << ';' << std::endl;
        }   
    }
    SymType *SynAssignOp::getType(SymTableInterface &tbl)
    {
        SymType *left = first_->getType(tbl);
        SymType *right = second_->getType(tbl);
        if(!first_->isLvalue())
            throw Errors::SyntaxError("left of " + binOp_ + " must be lvalue");
        if(binOp_ != "=")
        {
            BinOpOnTypes boot(AssignOp2BinOp[binOp_], BinOpTypePair(left->getSymbolType(), right->getSymbolType()));
            if(!AvailableBinOpsAndHandlers.count(boot))
                throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + left->getName() + ",right=" +
                right->getName() +')');
            right = AvailableBinOpsAndHandlers[boot](first_, second_, tbl);
        }
        BinOpOnTypes boot("=", BinOpTypePair(left->getSymbolType(), right->getSymbolType()));
        if(!AvailableBinOpsAndHandlers.count(boot))
            throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + left->getName() + ",right=" +
            right->getName() +')');
        return AvailableBinOpsAndHandlers[boot](first_, second_, tbl);              
    }

    SymType *SynBinaryOp::getType(SymTableInterface &tbl)
    {
        SymType *ft = first_->getType(tbl)->getType(), *st = second_->getType(tbl)->getType();
        BinOpOnTypes boot(binOp_, BinOpTypePair(ft->getSymbolType(),
            st->getSymbolType()));
        if(!AvailableBinOpsAndHandlers.count(boot))
            throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + ft->getName() + ",right=" +
            st->getName() +')');
        return AvailableBinOpsAndHandlers[boot](first_, second_, tbl);
    }

    SymType *SynUnaryOp::getType(SymTableInterface &tbl)
    { 
        UnaryOpOnType uoot(unOp_, first_->getType(tbl)->getSymbolType());
        if((unOp_ == "++" || unOp_ == "--" || unOp_ == "&") && !first_->isLvalue())
            throw Errors::SyntaxError("Operand of '" + unOp_ + "' must be lvalue");
        if(!AvailableUnaryOpsAndHandlers.count(uoot))
            throw Errors::SyntaxError("'" + unOp_ + '\'' + " undefined(operand=" + first_->getType(tbl)->getName() + ")");
        return AvailableUnaryOpsAndHandlers[uoot](first_, tbl);
    }


    void SynBinaryOp::print(std::ostream &stream, int ident)
    {
        second_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << binOp_ << '\n';
        first_->print(stream, ident + 1);
    }

    void SynUnaryOp::print(std::ostream &stream, int ident)
    {
        if(postfix_)
            first_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << unOp_<< '\n';
        if(!postfix_)
            first_->print(stream, ident + 1);
    }

    void SynSizeof::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "sizeof" << '\n';
        type_->print(stream, ident+1);
    }

    void SynFunctionCall::print( std::ostream &stream, int ident /*= 0*/ )
    {
        for(ArgList::reverse_iterator it = arguments_.rbegin(); it != arguments_.rend(); ++it)
            (*it)->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << "()" << '\n';
        caller_->print(stream, ident + 1);
    }

    void SynVal::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << identifier_ << '\n';
    }
    void SynInt::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << value_ << '\n';
    }
    void SynChar::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << value_ << '\n';
    }
    void SynFloat::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << value_ << '\n';
    }
    void SynString::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << value_ << '\n';
    }

    void SynStatementBlock::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << '{' << std::endl;
        makeIdent(stream, ident + 1);
        stream << "::internal table"<<std::endl;
        tbl_.print(stream, ident + 1);
        makeIdent(stream, ident + 1);
        stream << "::end of internal table"<<std::endl;
        for(size_t i = 0; i < content_.size(); ++i)
            content_[i]->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << '}' << std::endl;
    }

    void SynStatementIf::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "if" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        condition_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl;
        displayStmt(action_, stream, ident + 1);
    }
            
    void SynStatementPrintf::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "printf" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        for(size_t i = 0; i < args_.size() - 1; ++i)
        {
            args_[i]->print(stream, ident + 1);
            makeIdent(stream, ident);
            stream <<  "," << std::endl;
        }
        args_.back()->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl; 
    }

    void SynStatementIfWithElse::print(std::ostream &stream, int ident)
    {
        SynStatementIf::print(stream, ident);
        makeIdent(stream, ident);
        stream << "else" << std::endl;
        displayStmt(elseAction_, stream, ident + 1);
    }
    void SynStatementExpression::print(std::ostream &stream, int ident)
    {
        expr_->print(stream, ident);
    }
    void SynStatementReturn::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "return" << std::endl;
        if(expr_) expr_->print(stream, ident + 1);
    }
    void SynStatementBreak::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "break" << std::endl;
    }

    void SynStatementConinue::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "continue" << std::endl;
    }

    void SynStatementSwitch::print(std::ostream &stream, int ident)
    {
        makeIdent(stream, ident);
        stream << "switch" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        condition_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl;
        displayStmt(body_, stream, ident + 1);
    }
    void SynStatementDefault::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << "default:" << std::endl;
        displayStmt(statement_, stream, ident + 1);
    }
    void SynStatementCase::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << "case" << std::endl;
        label_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ':' << std::endl;
        displayStmt(statement_, stream, ident + 1);
        //statement_->print(stream, ident);      
    }

    void SynStatementDoWhile::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << "do" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        condition_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl;
        //body_->print(stream, ident + 1);
        displayStmt(body_, stream, ident + 1);
    }
    void SynStatementWhile::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << "do" << std::endl;
        displayStmt(body_, stream, ident + 1);
        makeIdent(stream, ident);
        stream << "while" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        condition_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl;
    }
    void SynStatementFor::print(std::ostream &stream, int ident /* = 0 */)
    {
        makeIdent(stream, ident);
        stream << "for" << std::endl;
        makeIdent(stream, ident);
        stream << '(' << std::endl;
        if(init_) init_->print(stream, ident + 1);
        makeIdent(stream, ident + 1);
        stream << ';' << std::endl;
        if(condition_) condition_->print(stream, ident + 1);
        makeIdent(stream, ident + 1);
        stream << ';' << std::endl;
        if(iter_) iter_->print(stream, ident + 1);
        makeIdent(stream, ident);
        stream << ')' << std::endl;
        displayStmt(body_, stream, ident + 1);
        //body_->print(stream, ident + 1);
    }

}

